﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using DiagramCreator.Nodes;
using System.Windows.Input;
using DiagramCreator.Arrows;
using System.Windows.Media;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media.Animation;

namespace DiagramCreator.Diagram
{
    public partial class DiagramSheet : Canvas, IDisposable
    {
        #region - AlignLeft -

        private void AlignLeft(List<GenericNode> nodes)
        {
            // znalezienie najbardziej wysuniętego w lewo elementu
            double x = 10000.0;
            foreach (GenericNode node in nodes)
            {
                if (x > (double)node.GetValue(Canvas.LeftProperty))
                {
                    x = (double)node.GetValue(Canvas.LeftProperty);
                }
            }
            foreach (GenericNode node in nodes)
            {
                node.SetValue(Canvas.LeftProperty, x);
                node.UpdateConnectionLines();
            }
        }

        #endregion

        #region - AlignRight -

        private void AlignRight(List<GenericNode> nodes)
        {
            // znalezienie najbardziej wysuniętego w prawo elementu
            double x = 0.0;
            foreach (GenericNode node in nodes)
            {
                if (x <= (double)node.GetValue(Canvas.LeftProperty) + (double)node.GetValue(Canvas.WidthProperty))
                {
                    x = (double)node.GetValue(Canvas.LeftProperty) + (double)node.GetValue(Canvas.WidthProperty);
                }
            }
            foreach (GenericNode node in nodes)
            {
                node.SetValue(Canvas.LeftProperty, x - (double)node.GetValue(Canvas.WidthProperty));
                node.UpdateConnectionLines();
            }
        }

        #endregion

        #region - AlignTop -

        public void AlignTop(List<GenericNode> nodes)
        {
            // znalezienie najbardziej wysuniętego w górę elementu
            double x = 10000.0;
            foreach (GenericNode node in nodes)
            {
                if (x > (double)node.GetValue(Canvas.TopProperty))
                {
                    x = (double)node.GetValue(Canvas.TopProperty);
                }
            }
            foreach (GenericNode node in nodes)
            {
                node.SetValue(Canvas.TopProperty, x);
                node.UpdateConnectionLines();
            }
        }

        #endregion

        #region - AlignBottom -

        public void AlignBottom(List<GenericNode> nodes)
        {
            // znalezienie najbardziej wysuniętego w dół elementu
            double x = 0.0;
            foreach (GenericNode node in nodes)
            {
                if (x < (double)node.GetValue(Canvas.TopProperty) + node.ActualHeight)
                {
                    x = (double)node.GetValue(Canvas.TopProperty) + node.ActualHeight;
                }
            }
            foreach (GenericNode node in nodes)
            {
                node.SetValue(Canvas.TopProperty, x - node.ActualHeight);
                node.UpdateConnectionLines();
            }
        }

        #endregion

        #region - AlignCenterVertiacally -

        private void AlignCenterVertiacally(List<GenericNode> nodes)
        {
            // znalezienie najbardziej wysuniętego w lewo elementu
            double x = 10000.0;
            double maxWidth = 0; // szerokosc najszerszego elementu
            foreach (GenericNode node in nodes)
            {
                if (x > (double)node.GetValue(Canvas.LeftProperty))
                {
                    x = (double)node.GetValue(Canvas.LeftProperty);
                }
                if (maxWidth < node.Width)
                {
                    maxWidth = node.Width;
                }

            }

            foreach (GenericNode node in nodes)
            {
                node.SetValue(Canvas.LeftProperty, x + (maxWidth - node.Width) / 2.0);
                node.UpdateConnectionLines();
            }
        }

        #endregion

        #region - AlignCenterHorizontally -

        private void AlignCenterHorizontally(List<GenericNode> nodes)
        {
            // znalezienie najbardziej wysuniętego do góry elementu
            double y = 10000.0;
            double maxHeight = 0; // wysokosc najwyzszego elementu
            foreach (GenericNode node in nodes)
            {
                if (y > (double)node.GetValue(Canvas.TopProperty))
                {
                    y = (double)node.GetValue(Canvas.TopProperty);
                }
                if (maxHeight < node.ActualHeight)
                {
                    maxHeight = node.ActualHeight;
                }

            }

            foreach (GenericNode node in nodes)
            {
                node.SetValue(Canvas.TopProperty, y + (maxHeight - node.ActualHeight) / 2.0);
                node.UpdateConnectionLines();
            }
        }

        #endregion

        #region - Distribute Vertiacally -

        const double MIN_VERTICAL_DISTANCE = - 15.0;

        private void DistributeVertically(List<GenericNode> nodes)
        {
            double height, startFrom; // wysokosc na jaka maja byc rozmieszczone elementy, oraz wysokosc poczatkowa
            double minimumHeight = 0.0;

            foreach (GenericNode node in nodes)            
                minimumHeight += (double)node.GetValue(Canvas.HeightProperty) + MIN_VERTICAL_DISTANCE;

            minimumHeight -= MIN_VERTICAL_DISTANCE;

            // sprawdzmy odleglosc od najwyzszego elementu do najnizszego plus ich wysokosci
            double actualHeight = 0.0;
            
            nodes.Sort((node1, node2) => (int)(((double)node1.GetValue(Canvas.TopProperty))
                 - ((double)node2.GetValue(Canvas.TopProperty))));
            GenericNode topNode = nodes[0];
            // zapisuje wysokosc poczatkowa:
            startFrom = (double)topNode.GetValue(Canvas.TopProperty);
            GenericNode bottomNode = nodes.Last();
            actualHeight = (double)bottomNode.GetValue(Canvas.TopProperty)
                 - (double)topNode.GetValue(Canvas.TopProperty)
                 + bottomNode.ActualHeight;
            // bieżemy wieksza z tych dwoch wartosci:
            if (actualHeight > minimumHeight)        
                height = (actualHeight - nodes.Sum(n => n.ActualHeight)) / (nodes.Count > 1 ? nodes.Count - 1: 1);            
            else
                height = MIN_VERTICAL_DISTANCE;

            foreach (GenericNode node in nodes)
            {
                node.SetValue(Canvas.TopProperty, startFrom);
                startFrom += node.ActualHeight + height;
                node.UpdateConnectionLines();
            }            
        }

        #endregion

        #region - Distribute Horizontally -

        const double MIN_HORIZONTAL_DISTANCE = 5.0;

        private void DistributeHorizontally(List<GenericNode> nodes)
        {
            if (nodes == null)
                return;
            if (nodes.Count == 0)
                return;

            double width;    // szerokosc na jaka maja byc rozmieszczone elementy
            double startFrom; // pozycja 'od lewej' poczatkowa
            double minimumWidth = 0.0;

            foreach (GenericNode node in nodes)
                minimumWidth += (double)node.GetValue(Canvas.WidthProperty) + MIN_VERTICAL_DISTANCE;

            minimumWidth -= MIN_HORIZONTAL_DISTANCE;

            // sprawdzmy odleglosc od najwyzszego elementu do najnizszego plus ich wysokosci
            double actualWidth = 0.0;

            nodes.Sort((node1, node2) => (int)(((double)node1.GetValue(Canvas.LeftProperty))
                 - ((double)node2.GetValue(Canvas.LeftProperty))));
            GenericNode leftNode = nodes[0];
            // zapisuje wysokosc poczatkowa:
            startFrom = (double)leftNode.GetValue(Canvas.LeftProperty);
            GenericNode rightNode = nodes.Last();
            actualWidth = (double)rightNode.GetValue(Canvas.LeftProperty)
                 - (double)leftNode.GetValue(Canvas.LeftProperty)
                 + rightNode.ActualWidth;
            // bieżemy wieksza z tych dwoch wartosci:
            if (actualWidth > minimumWidth)
                width = (actualWidth - nodes.Sum(node => node.ActualWidth)) / (nodes.Count > 1 ? nodes.Count - 1 : 1);
            else
                width = MIN_HORIZONTAL_DISTANCE;

            foreach (GenericNode node in nodes)
            {
                node.SetValue(Canvas.LeftProperty, startFrom);
                startFrom += node.ActualWidth + width;
                node.UpdateConnectionLines();
            }
        }

        #endregion
    }
}